<template>
  <div class="main-container1">
    <div class="main-card">
      <a href="/#/home">
        <span class="icon iconfont icon-shouye"></span>
      </a>
      <div class="card-header">
        <div class="header-title" style="position:relative;padding-left: 100px;"><img src="@/assets/images/top-logo.png"
            style="height:90px;position:absolute;top:-30px;left:20px;" alt=""><span v-if="homeConfig">{{
              homeConfig.interlocutionTitle }}</span>
        </div>
      </div>
      <div class="card-content">
        <div class="card-content-left">
          <el-drawer title="问答列表" :visible.sync="hisListDis" :show-close="false" size="20%" direction="ltr">
            <div class="history-list" v-if="historyList.length">
              <div class="history-List" v-for="item in historyList" @click="getThread(item.thread_id)"
                @mouseenter="hoverItem = item.thread_id" @mouseleave="hoverItem = null">
                <!-- 重命名输入框 -->
                <el-input v-if="renamingId === item.thread_id" v-model="editText" ref="renameInputLeft"
                  @blur="submitRename(item)" @keyup.enter="submitRename(item)" @keyup.esc="cancelRename" size="mini"
                  style="width: 80%"></el-input>
                <div v-else class="history-title" :title="item.metadata.ques">
                  {{ item.metadata.ques }}
                </div>

                <el-dropdown trigger="click" v-show="hoverItem === item.thread_id">
                  <span class="el-dropdown-link">
                    <i class="el-icon-more"></i>
                  </span>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item icon="el-icon-edit" @click.native.stop="startRename(item)">重命名</el-dropdown-item>
                    <el-dropdown-item icon="el-icon-delete"
                      @click.native="deleteHistory(item.thread_id)">删除</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </div>
            </div>

          </el-drawer>

          <!-- 配置弹窗 -->
          <el-drawer :visible.sync="setDis" :show-close="false" size="20%" direction="rtl">
            <div style="padding:25px 20px;">
              <div>
                <span class="demonstration">知识库返回限制</span>
                <el-slider style="width:90%;margin:0 auto;" v-model="localLimit" :step="1" :min="1" :max="10"
                  :marks="marksLimit" show-stops>
                </el-slider>
              </div>

              <!-- <el-divider />
        <div>
          <span class="demonstration">本地知识库</span>
          <el-row style="padding:18px 40px;" class="auto-audio-role">
            <span class="demonstration" style="color:#72767b;">是否调用</span>
            <el-switch style="float:right;"
              v-model="isLocalData">
            </el-switch>
          </el-row>
        </div> -->

              <el-divider />
              <div>
                <span class="demonstration">特色人声朗读</span>
                <el-row style="padding:18px 40px;" class="auto-audio-role rensheng">
                  <el-button v-for="item in audioRole" :type="item.sex == 'F' ? 'danger' : 'primary'"
                    :plain="!item.checked" @click="audioRoleUp(item)"><img :src="item.url" />{{ item.label
                    }}</el-button>
                </el-row>

                <el-row style="padding:18px 40px;" class="auto-audio-role">
                  <span class="demonstration" style="color:#72767b;">开启朗读</span>
                  <el-switch style="float:right;" v-model="isSetAudio">
                  </el-switch>
                </el-row>

              </div>

              <!-- <div>
            <div style="padding-bottom: 10px;"><span class="demonstration">切换源</span></div>
            <el-radio-group v-model="fetchKey">
              <el-radio-button label="local" key="local">本地部署</el-radio-button>
              <el-radio-button label="deepseek" key="deepseek">云端对话</el-radio-button>
            </el-radio-group>
          </div> -->
            </div>
          </el-drawer>

          <!-- 右侧主界面 -->
          <div class="main-content">
            <div v-if="currentMessages.length < 2" style="position:absolute;top:35%;left:0;right:0;">
              <img src="@/assets/images/AIBg.png" alt=""
                style="width: 20%;margin:0 auto;display:block;width:150px;opacity: 0.08;">
              <div style="width:230px;margin:0 auto;color:#888;font-size: 15px;margin-top: 15px;text-align: center;">
                我可以帮你搜索、答疑，请把你的任务交给我吧~</div>
            </div>
            <!-- 消息容器 -->
            <div class="message-container" ref="messageContainer">
              <div v-for="(message, index) in currentMessages" :key="index" class="message-item" :class="message.type"
                v-if="message.type != 'tool' && message.name != 'local' && message.content.length">
                <div class="message-avatar" v-if="message.type === 'ai'"><img :src="audioRoleKey.url" alt="AI"></div>
                <!-- <div class="message-avatar pc" v-else><img :src="avatar" alt="User"></div> -->

                <div class="message-bubble">
                  <div v-if="message.type === 'ai' && message.generating" style="display: flex;margin-bottom: 10px;">
                    <div class="loading-spinner">
                      <div class="spinner"></div>
                    </div>
                    <div style="line-height:30px;padding-left:15px;font-size: 15px;color: #727272;"> <span>思考中...
                      </span></div>
                  </div>

                  <div class="message-text" v-html="upContent(message.content)"></div>

                  <!-- 音频按钮 -->
                  <el-button v-if="message.type === 'ai' && !message.generating" style="width:30px;margin:5px 0;"
                    class="play-button" :type="isAudioStart.id == index && isAudioStart.is ? 'danger' : 'success'"
                    :icon="isAudioStart.id == index && isAudioStart.is ? 'el-icon-video-pause' : 'el-icon-video-play'"
                    circle size="mini"
                    @click="isAudioStart.id == index && isAudioStart.is ? audioStop(index) : audioStart(index)" />
                  <div v-if="message.finish" v-for="resource in message.resourceList"
                    style="margin-top: 10px;background: #edeef3;">
                    <!-- 图片展示 -->
                    <div v-if="resource.type === 'image'" style="padding: 20px;">
                      <div class="message-title">{{ resource.title }}</div>
                      <div class="message-content">
                        <el-image v-if="resource.isArray" v-for="item in resource.url" :style="{ width: resource.width, padding: '10px' }" style="max-width: 100%;" :src="item" :preview-src-list="resource.url">
                        </el-image>
                        <div v-if="!resource.isArray">
                          <el-image :style="{ width: resource.width, padding: '10px' }" style="max-width: 100%;" :src="resource.url"
                            :preview-src-list="[resource.url]">
                          </el-image>
                        </div>
                      </div>
                    </div>
                    <!-- PDF展示 -->
                    <div v-else-if="resource.type === 'pdf'" style="padding: 20px;">
                      <div class="message-title">{{ resource.title }}</div>
                      <div v-if="resource.isArray" v-for="item in resource.url">
                        <pdf-viewer :url="item" :width="resource.width"></pdf-viewer>
                        <div style="margin-top: 10px;text-align: center;">
                          <a :href="item" target="_blank" style="color: #409EFF; text-decoration: underline;">
                            下载原文件
                          </a>
                        </div>
                      </div>
                      <div v-if="!resource.isArray">
                        <pdf-viewer :url="resource.url" :width="resource.width"></pdf-viewer>
                        <div style="margin-top: 10px;text-align: center;">
                          <a :href="resource.url" target="_blank" style="color: #409EFF; text-decoration: underline;">
                            下载原文件
                          </a>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div v-if="message.name === 'system'" class="">
                    <el-divider></el-divider>
                    <div class="rainbow-text" style="padding-bottom:15px;"><span class="icon iconfont icon-dengpao1"
                        style="width:25px;color: #5151EE;"></span>&emsp;猜你想问</div>
                    <div class="p-div p-dian" style="color:#1890ff">出生证明怎么办理？</div>
                    <div class="p-div p-dian" style="color:#1890ff">社保怎么补办？</div>
                  </div>
                </div>
              </div>
            </div>


            <!-- 输入区域 -->
            <div class="input-div">
              <div class="input-bg">
                <div v-if="inputStatus == 'yuyin'" style="position:relative;min-height: 34px;">
                  <div v-if="!btnStopDis && inputMessage" style="padding-bottom:10px;color:#606266;">{{ inputMessage }}
                  </div>
                  <button @click="upInputStatus('dazi')" class="btnNone"><span
                      class="iconBtn icon iconfont icon-icon-dazi" style="font-size: 25px;" /></button>
                  <el-button v-if="!btnStartDis" @click="record" :disabled="btnStartDis" type="primary" size="mini"
                    round plain style="position:absolute;left:calc(50% - 76px);width:150px;bottom:2px;">点击
                    说话</el-button>
                  <el-button v-if="!btnStopDis" @click="stop" :disabled="btnStopDis" type="danger" size="mini" round
                    plain style="position:absolute;left:calc(50% - 76px);width:150px;bottom:2px;">点击 停止</el-button>
                </div>

                <div v-else style="display:flex;min-height: 40px;">
                  <button @click="upInputStatus('yuyin')" class="btnNone"><span
                      class="iconBtn icon iconfont icon-yuyin1" style="font-size: 29px;" /></button>
                  <el-input class="inputDivNone" :disabled="!isEnd" type="textarea"
                    :autosize="{ minRows: 1, maxRows: 5 }" :clearable="true" :placeholder="isEnd ? '输入你的问题' : ''"
                    v-model="inputMessage" @keydown.enter.native="submitQues" resize="none">
                  </el-input>
                  <el-button class="btnNone" v-if="!isEnd" size="medium" @click="cancelRequest()"><span
                      class="iconBtn icon iconfont icon-16gf-stopCircle" style="font-size: 29px;" /></el-button>
                  <el-button class="btnNone" v-if="isEnd" size="medium" @click="submitQues()"><span
                      class="iconBtn icon iconfont icon-fasonganniu"
                      style="font-size: 29px;color:#0395F8;" /></el-button>
                </div>
              </div>

              <div style="padding:10px 5px 0;">
                <el-button
                  style="padding:2px 10px;border:2px solid #9D9D9D;background:none;border-radius: 20px;float:left;"
                  class="" size="medium" @click="createNewSession">
                  <span class="iconBtn icon iconfont icon-a-lujing53834"
                    style="font-size: 18px;color:#0395F8;float:left;width:auto;height:auto;"></span>
                  <div style="line-height: 20px;font-size: 13px;padding-left:25px;color:#999;">新对话</div>
                </el-button>


                <div style="float:right;">
                  <el-button class="" style="border:0;padding:0;background:none;float:left;margin-top: 2px;"
                    @click="hisListDis = true"><span class="iconBtn icon iconfont icon-caidan1"
                      style="font-size: 24px;" /></el-button>
                  <el-button style="border:0;padding:0;background:none;" size="medium" @click="setDis = true"><span
                      class="iconBtn icon iconfont icon-shezhi" style="font-size: 29px;" /></el-button>
                </div>
              </div>

            </div>


          </div>
          <div class="stAudio">
            <audio id="myAudio" :controls="true" />
          </div>

          <!-- <div ref="videoContainer" class="stAudio" style="top:60%;width: 300px;"></div> -->
        </div>
        <div class="card-content-right">
          <div>
            <div class="right-title">
              便民服务
            </div>
            <div class="right-content">
              <el-button v-for="item in bianminD" class="bianminBtn btnNone" type="primary"
                :style="'position:relative;background:#' + item.bg + ' ;'"><span class="icon iconfont"
                  :class="item.icon" style="color:#fff;position: absolute;top:1px;left:15px;"></span>{{ item.title
                  }}</el-button>
            </div>
          </div>
          <div>
            <div class="right-title">
              特色创新服务
            </div>
            <div class="right-content">
              <div v-for="item in serveD" class="serveDiv">
                <div style="height:45px;text-align: center;">
                  <a>
                    <span v-if="item.icon" class="icon iconfont" :class="item.icon"
                      :style="'color:#' + item.color + ';font-size:40px;display:block;'"></span>
                    <img v-else :src="item.img" alt="" style="width:40px;height:40px;">
                  </a>
                </div>

                {{ item.title }}
              </div>

            </div>
          </div>
          <div>
            <div class="right-title">
              历史记录
            </div>
            <div class="right-content" style="max-height: 40vh; overflow-y: auto;" v-if="historyList.length">
              <a>
                <div class="history-List" style="border-radius:50px 0 0 50px;" v-for="item in historyList"
                  @click="getThread(item.thread_id)" @mouseenter="hoverItem = item.thread_id"
                  @mouseleave="hoverItem = null">
                  <!-- 重命名输入框 -->
                  <el-input v-if="renamingId === item.thread_id" v-model="editText" ref="renameInputRight"
                    @blur="submitRename(item)" @keyup.enter="submitRename(item)" @keyup.esc="cancelRename" size="mini"
                    style="width: 80%"></el-input>
                  <div v-else class="history-title" :title="item.metadata.ques">
                    {{ item.metadata.ques }}
                  </div>

                  <el-dropdown trigger="click" v-show="hoverItem === item.thread_id">
                    <span class="el-dropdown-link">
                      <i class="el-icon-more"></i>
                    </span>
                    <el-dropdown-menu slot="dropdown">
                      <el-dropdown-item icon="el-icon-edit"
                        @click.native.stop="startRename(item, true)">重命名</el-dropdown-item>
                      <el-dropdown-item icon="el-icon-delete"
                        @click.native="deleteHistory(item.thread_id)">删除</el-dropdown-item>
                    </el-dropdown-menu>
                  </el-dropdown>
                </div>
              </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>



<script>


import { mapGetters } from 'vuex'
import Recorder from 'recorder-core';
import '@/assets/js/wav';
import '@/assets/js/pcm';


// 数据接口
import { getData } from "@/api/localserver/data";
import { getIpConfig } from "@/api/wenda/ipConfig";
import { addConfig } from "@/api/wenda/jsonConfig";
import { getConfig } from "@/api/wenda/homeConfig";

// 分割后的js
import tospeech from '@/api/wenda/tospeech.js'; // 引入 tospeech.js
import markdown from '@/utils/wenda/markdown.js'; // 引入定义样式后的markdown
import wss from "@/api/wenda/wss.js"; // 引入初始化方法
import langC from '@/api/wenda/langChain.js';
import docs from '@/api/wenda/docs.js'; // 引入 tospeech.js

// 引用图片
import aiNv from '@/assets/images/ai-role/nv.jpg';
import aiNan from '@/assets/images/ai-role/nan.jpg';
import kuaisuImg from '@/assets/images/icon-kuaisu.png';
import { block } from 'marked/src/rules';
import PdfViewer from './PdfViewer.vue'





export default {
  components: {
    PdfViewer
  },
  data() {
    return {
      currentMessages: [],
      // 页面补充数据
      bianminD: [{ title: "社保中心", icon: "icon-shebaobutie", bg: "84CFF6", url: "", click: "" }, { title: "公积金", icon: "icon-a-ziyuan23", bg: "84CFF6", url: "", click: "" }, { title: "人社局", icon: "icon-zhengfu", bg: "7FA3F7", url: "", click: "" }, { title: "残联", icon: "icon-canlian", bg: "7FA3F7", url: "", click: "" }, { title: "住建局", icon: "icon-zhujianjulogo", bg: "7F86FB", url: "", click: "" }, { title: "民政", icon: "icon-minzhengtubiao1-48", bg: "7F86FB", url: "", click: "" },],
      serveD: [{ title: "场景服务", icon: "icon-qitagongnengsvg", color: "FAAC6F", url: "", click: "" }, { title: "高效办成一件事", icon: null, img: kuaisuImg, color: "7F86FB", url: "", click: "" }, { title: "全生命周期", icon: "icon-xunhuan", color: "5AE8C2", url: "", click: "" }, { title: "一业一证", icon: "icon-yinzhangjieyong", color: "84CFF6", url: "", click: "" },],
      // texttospeech  
      audioRole: [{ "voice": 1088, checked: false, label: "女声01", "prompt": "开心", sex: "F", url: aiNv, "rm": "正常" }, { "voice": 102, checked: false, label: "女声02", "prompt": "开心", sex: "F", url: aiNv, "rm": "正常，太绵" }, { "voice": 1422, checked: false, label: "女声03", "prompt": "开心", sex: "F", url: aiNv, "rm": "机器味" }, { "voice": 7000, checked: false, label: "男声", "prompt": "开心", sex: "M", url: aiNan, "rm": "正常，呆" },],
      audioRoleKey: {},
      // 播放、转音频参数
      audioSrc: [], toSpeechtest: /[？。！（]/, toSpeechKey: 0, audioAutoKey: 0, isSetAudio: true, isAutoAudio: false, audioOdj: null, isAudioStart: { id: 0, is: false },
      // 语音
      inputStatus: "dazi", Uri: "/wssapi/", btnStartDis: true, btnStopDis: true, wsconnecter: null, isRec: false,
      use_itn: false, asr_mode: "2pass", isfilemode: false, sampleBuf: new Int16Array(), file: "", hotwords: { "阿里巴巴": 20, "hello world": 40 }, chunk_size: new Array(5, 10, 5), rec_text: "", offline_text: "",
      // 问答配置
      fetchKey: "local",
      fetchConfig: {
        deepseek: { url: "/deep/chat/completions", model: "deepseek-reasoner", apikey: 'Bearer sk-df0831cae1e446e3a1b20e32d6e597f9' },
        local: { url: "https://api.siliconflow.cn/v1/chat/completions", model: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", apikey: 'Bearer sk-taalmohasunalwcsqnjgpnsfdxqeuofkhnbimbmzlcptdgld' }
      },
      marksLimit: { 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10' },
      localLimit: 3,
      // 问答模块
      selVal: {},
      // 页面问答基础数据
      hisListDis: false, setDis: false, inputMessage: '', historyList: [], currentSessionId: null,
      // 流式消息
      isEnd: true, isUserEnd: false, decoder: null, isThink: false, isContent: false,
      // 使用LangChain
      client: null, isLocalData: false, thread: null, agent: null, uuid: null, key: null,

      // 新增重命名相关状态
      renamingId: null,
      editText: '',
      originalText: '',
      hoverItem: null,
      homeConfig: {
        interlocutionTitle: ''
      },
    }

  },
  // computed: {
  //   ...mapGetters(['avatar']),

  // },
  created() {
    this.init()
  },
  mounted() {
  },
  watch: {
    isSetAudio(newVal, oldVal) { localStorage.setItem('wdSet-isSetAudio', newVal ? '1' : '') },
    localLimit(newVal, oldVal) { localStorage.setItem('wdSet-localLimit', newVal) },
    audioRoleKey(newVal, oldVal) { localStorage.setItem('wdSet-audioRoleKey', JSON.stringify(newVal)) },
    // isLocalData(newVal, oldVal) {localStorage.setItem('wdSet-isLocalData', newVal?'1':'')}

  },
  methods: {

    init() {

      // 定义一个utf-8格式的文本解码器
      this.decoder = new TextDecoder('utf-8')
      // 获取本地存储的值
      localStorage.getItem('wdSet-localLimit') !== null && (this.localLimit = +localStorage.getItem('wdSet-localLimit'))
      localStorage.getItem('wdSet-isSetAudio') !== null && (this.isSetAudio = Boolean(localStorage.getItem('wdSet-isSetAudio')))
      // localStorage.getItem('wdSet-isLocalData') !== null && (this.isLocalData = Boolean(localStorage.getItem('wdSet-isLocalData')))



      // 从 localStorage 获取音频角色配置
      const rolekey = localStorage.getItem('wdSet-audioRoleKey')
      console.log(rolekey)
      const audioRoleData = rolekey ? JSON.parse(rolekey) : this.audioRole[0]
      this.audioRoleUp(audioRoleData)


      let id = this.$route.query.id

      if (id) {
        getConfig(id).then(res => {
          this.homeConfig = res.data
          this.selVal = { value: res.data.key, label: res.data.title, msg: res.data.cueWord }
          this.initLang()
        })
      }
      getIpConfig().then(res => {
        this.uuid = res.data.uuid
        console.log("生成唯一uuid", this.uuid)
        if (!this.uuid) {
          this.$modal.msgWarning("唯一ID未生成")
        }
      })
    },
    async initLang() {
      this.currentMessages = [{ content: this.selVal.msg, type: 'ai', name: 'system' }]

      // this.assistants = new AssistantsClient(config);
      // this.threads = new ThreadsClient(config);
      // this.runs = new RunsClient(config);
      // this.crons = new CronsClient(config);
      // this.store = new StoreClient(config);

      const assistants = await langC.agentList({ graphId: "retrieval_graph", limit: 999 }) // indexer
 
      if (!assistants) {
        // this.agent = await langC.agentCreate({ graphId: "agent" })
        this.$modal.msgError("AI助手链接出错，请检查网络或服务")
        return false
      } else {
        if (this.homeConfig.assisId) {

          for (let i = 0; i < assistants.length; i++) {
            const item = assistants[i];
            if (item.assistant_id == this.homeConfig.assisId) {
              this.agent = item;
              break; // 找到匹配项后立即退出循环
            }
          }
        } else {
          this.agent = assistants[0]
        }
      }
      console.log("agent:", this.agent)
      this.historyList = await langC.threadList({ status: "idle", offset: 0, limit: 100, metadata: { uuid: this.uuid, type: "user", key: this.selVal.value, assistant_id: this.agent.assistant_id } })

      // 初始化 WebSocket 连接
      this.wsconnecter = wss.initWebSocket({ file: this.file, stateHandle: this.getConnState, msgHandle: this.getJsonMessage, use_itn: this.use_itn, asr_mode: this.asr_mode, isfilemode: this.isfilemode, hotwords: this.hotwords });
      // 最后初始 开屏交互后播放语音
      this.fetchToSpeech({ text: "", to_i: -1, msg_i: 0, voice: this.audioRoleKey.voice, prompt: this.audioRoleKey.prompt, msg_content: this.selVal.msg, all: true, toSpeechtest: this.toSpeechtest, form: "audioStart", })

    },
    // 执行文字转语音的方法
    async fetchToSpeech(data) {
      data.text = data.text.replace('并且以中文回复，回复内容中不包含附件参考信息', '');
      let res = await tospeech.sendPostRequestApi(data);
      if (res.audioInit) {
        this.audioStop(res.msg_i)
        this.audioInit(res.msg_i)
      }
      this.audioSrc[res.to_i] = res.audioSrc;
      // 检查是否需要启动音频播放，且确保只调用一次播放音频
      if (res.audioStart && !this.audioAutoKey && !this.isAutoAudio) {
        // 如果 res.audioStart 为 true，并且当前没有自动播放音频的状态，则执行以下操作
        // 更新 isAudioStart 状态，标记当前消息的音频播放状态
        this.isAudioStart = res.isAudioStart;
        // 将返回的音频源数据存储到 audioSrc 数组中，索引为 res.to_i
        console.log("朗读前打印 sendPostRequest001 ：", this.audioSrc);
        // 调用 audioStart 方法，启动指定消息索引的音频播放
        this.audioStart(res.msg_i);
      }
      if (this.currentMessages[res.msg_i] && res.all) {
        this.toSpeechKey++
        // console.log("重新调用：",'',this.toSpeechKey,msg_i,true)
        res.audioInit = false
        res.audioSrc = {}
        res.audioStart = false
        res.to_i++
        console.log("重新调用", res)
        this.fetchToSpeech(res)
      }
      console.log(this.audioSrc)
    },
    // 播放音频
    audioStart(msg_i) { // 自动播放音频
      (msg_i != this.isAudioStart.id)
      // console.log("audioStart --问答下标：",msg_i,"当前数据：",this.isAudioStart,"--语音数据：",this.audioSrc,"--播放下标：",this.audioAutoKey)
      if (msg_i != this.isAudioStart.id && this.isEnd) { // 数据不匹配，没有正在回答的问题
        console.log("audioStart,重新加载语音数据")
        // this.sendPostRequest('',-1,msg_i,true,"audioStart")
        this.fetchToSpeech({
          text: "",
          to_i: -1,
          msg_i: msg_i,
          voice: this.audioRoleKey.voice,
          prompt: this.audioRoleKey.prompt,
          msg_content: this.currentMessages[msg_i].content,
          all: true,
          toSpeechtest: this.toSpeechtest,
          form: "audioStart",
        })
        return false
      }
      // 自动播放音频
      if (!this.audioSrc[this.audioAutoKey] || !this.audioSrc[this.audioAutoKey].src) { // 当前播放音频为空，重新播放，直至音频存在
        // console.log("audioStart==this.audioSrc[this.audioAutoKey]==",this.audioSrc[this.audioAutoKey])
        setTimeout(() => {
          this.audioStart(msg_i)
        }, 1500);
        return false
      }
      this.audioOdj = new Audio(this.audioSrc[this.audioAutoKey].src);
      this.audioOdj.addEventListener('ended', () => {
        setTimeout(() => {
          this.audioAppend(msg_i)
        }, 500);
      });
      this.audioOdj.controls = true
      this.audioOdj.id = "myAudio";
      this.audioOdj.className = "stAudio";
      document.getElementById('myAudio').replaceWith(this.audioOdj)

      this.audioOdj.play().then(() => {
        this.isAutoAudio = true
        // console.log("play",botMessage)
        this.isAudioStart = { id: msg_i, is: true }

        console.warn("语音开始播放 ******* 当前KEY", this.audioAutoKey, "总长：", this.audioSrc.length, this.audioSrc[this.audioAutoKey].text)
      }).catch(error => {
        console.error("播放失败：问答下标：", msg_i, "--当前播放KEY：", this.audioAutoKey, error);
        console.log("语音数据：", JSON.stringify(this.audioSrc[this.audioAutoKey]));
        this.isAudioStart = { id: msg_i, is: false }
        setTimeout(() => {
          this.audioStart(msg_i)
        }, 2000);
      });
    },
    audioAppend(msg_i) { // 播放音频追加调用
      if (!msg_i) {
        console.warn("播放完成")
        this.isAudioStart = { id: 0, is: false }
        this.audioInit(msg_i)
        return false
      }

      if (this.audioSrc[this.audioAutoKey] && this.audioSrc[this.audioAutoKey].src) { // 确认信息后删除
        URL.revokeObjectURL(this.audioSrc[this.audioAutoKey].src); // 释放已经播放完成的音频
      }
      let msgArr = this.currentMessages[msg_i].content.split(this.toSpeechtest);
      console.log("audioAppend-追加播放时对比长度：msg_i：" + msg_i
        + "，audioAutoKey：" + this.audioAutoKey
        + "，audioSrc.length：" + this.audioSrc.length
        + "，msgArr.length：" + msgArr.length)
      // msgArr
      // 当前播放下标超过实际文字截取后的长度，才会停止
      if (this.isEnd) {
        // 比对数据长度
        if (this.audioAutoKey >= this.audioSrc.length - 1 && this.audioSrc.length >= msgArr.length - 1) {
          // console.warn("audioAppend-setTimeout",msgArr,msgArr.length)
          console.warn("播放完成")
          this.isAudioStart = { id: msg_i, is: false }
          // console.log("&&&&&&&audioAppend修改isAudioStart",this.isAudioStart)
          this.audioInit(msg_i)
          return false
        }
      }
      setTimeout(() => {
        this.audioAutoKey++ //播放音频
        this.audioStart(msg_i)
      }, 500);
    },
    audioStop(msg_i) {
      this.isAudioStart = { id: msg_i, is: false }
      if (this.audioOdj) {
        this.audioOdj.pause();
        this.audioOdj = null;
      }
    },
    audioInit(msg_i) {
      console.log("audioInit", msg_i)
      this.audioOdj = null
      this.toSpeechKey = 0
      this.audioAutoKey = 0
      this.isAutoAudio = false
      this.audioSrc = [] // blob数据已经逐个释放
      this.isAudioStart = { id: 0, is: false }
    },
    audioRoleUp(data) {
      this.audioRoleKey = data
      for (let i = 0; i < this.audioRole.length; i++) {
        const voice = this.audioRole[i].voice;
        if (voice == this.audioRoleKey.voice) {
          console.log(voice)
          this.audioRole[i].checked = true
        } else {
          this.audioRole[i].checked = false
        }
      }
    },
    // 录音开始
    upInputStatus(st) {
      if (this.isRec) {
        this.$modal.msgWarning("录音中，终止后使用语音功能");
        return false
      }
      if (!this.isEnd) {
        this.$modal.msgWarning("回答中，终止后可使用语音功能");
        return false
      }
      this.inputStatus = st
      if (st == 'yuyin') {
        this.start()
      }
    },
    async record() { // (2)(点击录音/获取权限)点击按钮，开始调起录音
      const recorder = new Recorder({
        type: "pcm",
        bitRate: 16,
        sampleRate: 16000,
        numChannels: 1,  // 

        audioTrackSet: { // 新增音频轨道配置
          echoCancellation: true,    // 启用回声消除
          noiseSuppression: true,    // 启用噪声抑制
          autoGainControl: false,      // 禁用自动增益控制
          channelCount: 1 // 单声道
        },

        // echoCancellation: true,    // 启用回声消除
        // noiseSuppression: true,    // 启用环境噪声抑制
        // autoGainControl: true,     // 启用自动增益控制
        onProcess: this.recProcess,
      });
      recorder.open(() => {
        this.isRec = true
        this.btnStartDis = true;
        this.btnStopDis = false;
        console.log("record()(2)(点击录音/获取权限)开始录音")
        recorder.start();

      }, (error) => { // 新增错误回调参数
        // this.$modal.msgWarning(error);
        this.inputStatus = "dazi"
        this.$modal.msgError(error);
      });
      this.rec = recorder;

    },
    start() { // (1)(链接)识别启动、停止、清空操作
      this.clear();
      // 启动连接
      if (this.wsconnecter && typeof this.wsconnecter.wsStart === 'function') {
        try {
          var ret = this.wsconnecter.wsStart(this.Uri);
          // 1 is ok, 0 is error
          if (ret == 1) {
            console.log("进入start()(1)(链接)正常开始")
            // this.$modal.msgWarning("正在连接asr服务器，请稍后...")
            // this.inputStatus="dazi"
            this.btnStartDis = false;
            this.btnStopDis = true;
            console.log(401, this.btnStopDis)

            return 1;
          } else {
            console.log("进入start()出错，无法开始，连接失败")
            this.$modal.msgError("无法连接asr服务器，请重试")
            this.inputStatus = "dazi"
            this.btnStartDis = true;
            this.btnStopDis = true;
            console.log(411, this.btnStopDis)
            return 0;
          }
        } catch (error) {
          this.inputStatus = "dazi"
          this.btnStartDis = true;
          this.btnStopDis = true;
          console.log(418, this.btnStopDis)
          this.$modal.msgError(error)
          console.error('wsStart 调用失败: WebSocket 连接启动失败，请重试:', error);
        }
      } else {
        this.inputStatus = "dazi"
        this.btnStartDis = true;
        this.btnStopDis = true;
        console.log(428, this.btnStopDis)
        this.$modal.msgError("未正确初始化")
        console.error('wsconnecter 未正确初始化或 wsStart 方法不存在:WebSocket 连接器未正确初始化，请重试');
      }
    },
    stop() { // 停止连接
      if (this.wsconnecter && typeof this.wsconnecter.wsStop === 'function') {

        try {
          // 控件状态更新

          var request = {
            "chunk_size": this.chunk_size,
            "wav_name": "h5",
            "is_speaking": false,
            "chunk_interval": 10,
            "mode": this.asr_mode,
          };
          console.log(request);
          if (this.sampleBuf.length > 0) {
            this.wsconnecter.wsSend(this.sampleBuf);
            console.log("sampleBuf.length" + this.sampleBuf.length);
            this.sampleBuf = new Int16Array();
          }
          this.wsconnecter.wsSend(JSON.stringify(request));

          console.log("发送完数据,请等候,正在识别...");
          this.isRec = false;


          if (!this.isfilemode) {
            this.inputStatus = 'dazi' // 停止录音，切回打字状态
            this.btnStopDis = true;
            console.log(442, this.btnStopDis)
            this.btnStartDis = true;
            // 等待 3s 后断开 WebSocket
            setTimeout(() => {
              console.log("call stop ws!");
              this.wsconnecter.wsStop();
              console.log("延时三秒断开，请点击连接");
            }, 3000);

            // 停止录音
            if (this.rec && typeof this.rec.stop === 'function') {
              this.rec.stop((blob, duration) => {
                console.log("停止录音,打印录音文件", blob);
                var audioBlob = Recorder.pcm2wav({
                  sampleRate: 16000,
                  bitRate: 16,
                  blob: blob
                }, (theblob, duration) => {
                  console.log(theblob);
                  let src = (window.URL || webkitURL).createObjectURL(theblob);
                  console.log(src)
                }, (msg) => {
                  console.log(msg);
                });
              }, (errMsg) => {
                console.log("errMsg: " + errMsg);
              });
            }
          }
        } catch (error) {
          console.error('WebSocket 连接停止失败，请重试  wsStop 调用失败:', error);
        }
      } else {
        console.error('wsconnecter 未正确初始化或 wsStop 方法不存在');
      }
    },
    getConnState(connState) { // 连接状态响应
      console.log("connState", connState);
      if (connState === 0) { // on open
        console.log('getConnState  连接成功!请点击开始');
        this.inputStatus = "yuyin"
        this.btnStartDis = false;
        this.btnStopDis = true;
      } else if (connState === 1) {
      } else if (connState === 2) {
        this.stop();
        console.log("连接地址" + this.Uri + "失败,请检查asr地址和端口。或试试界面上手动授权，再连接。");
        this.btnStartDis = true;
        this.btnStopDis = true;
        // console.log(499,this.btnStopDis)
      }
    },
    clear() { // 重置
      // var varArea=document.getElementById('varArea');
      this.inputMessage = "";
      this.rec_text = "";
      this.offline_text = "";
    },
    getJsonMessage(jsonMsg) { // 获取识别结果
      console.log("###################getJsonMessage: " + JSON.parse(jsonMsg.data)['text']);
      var rectxt = "" + JSON.parse(jsonMsg.data)['text'];
      var asrmodel = JSON.parse(jsonMsg.data)['mode'];
      var timestamp = ''; // 返回语音录制的秒数，目前在ws下有值，wss未定义
      if (asrmodel == "2pass-offline" || asrmodel == "offline") {
        this.offline_text = this.offline_text + wss.handleWithTimestamp(rectxt, timestamp); // rectxt; //.replace(/ +/g,"");
        this.rec_text = this.offline_text;
      } else {
        this.rec_text = this.rec_text + rectxt; //.replace(/ +/g,"");
      }
      this.inputMessage = this.rec_text;
      console.log("rec_text: " + this.rec_text);
    },
    recProcess(buffer, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) { // （麦克风）实时处理录音数据                                                                                                                                                                                                                                          
      this.sampleBuf = new Int16Array()
      let sendBuf
      if (this.isRec === true) {
        var data_48k = buffer[buffer.length - 1];
        var array_48k = new Array(data_48k);
        var data_16k = Recorder.SampleData(array_48k, bufferSampleRate, 16000).data;
        this.sampleBuf = Int16Array.from([...this.sampleBuf, ...data_16k]);
        var chunk_size = 960; // for asr chunk_size [5, 10, 5]
        while (this.sampleBuf.length >= chunk_size) {
          sendBuf = this.sampleBuf.slice(0, chunk_size);
          this.sampleBuf = this.sampleBuf.slice(chunk_size, this.sampleBuf.length);
          try {
            this.wsconnecter.wsSend(sendBuf);
          } catch (error) {
            this.stop();
          }
        }
      }
    },
    // 录音结束

    // 新问题
    async createNewSession() {
      // 新问题首先发消息
      this.currentMessages = []
      this.historyList = await langC.threadList({ status: "idle", offset: 0, limit: 100, metadata: { uuid: this.uuid, type: "user", key: this.selVal.value, assistant_id: this.agent.assistant_id } })
    },
    // 填充当前问题的历史内容currentSession
    async getThread(id) {
      this.thread = await langC.threadGet(id)
      this.currentMessages = this.thread.values.messages
      // console.log("getThread:",this.thread)
    },
    async submitQues() {
      if (!this.inputMessage.length) {
        this.$modal.msgWarning("请输入有效的提问信息");
        return false
      }
      // 正在执行？
      if (!this.isEnd) {
        this.$modal.msgWarning("请终止现有提问");
        return false
      }
      let msgArr = [{ type: "human", name: "user", content: this.inputMessage, assistant_id: this.agent.assistant_id }, { type: "ai", name: "", content: " ", generating: true }]
      // 重置页面的数据和状态
      this.inputMessage = ''
      this.isEnd = false
      this.isUserEnd = false

      if (this.currentMessages.length == 1) { // 第一次提交问题
        this.currentMessages = msgArr
      } else {
        this.currentMessages.push(...msgArr)
      }
      console.log("currentMessages:", this.currentMessages)
      // 提交问题后，带入线程,无线程时,重新生成
      if (!this.thread) {
        this.thread = await langC.threadCreate({
          "thread_id": "",
          "metadata": { graph_id: "agent", ques: msgArr[0].content, uuid: this.uuid, type: "user", key: this.selVal.value },
          "if_exists": "raise",
        })
      }
      // 语音预回答
      this.fetchToSpeech({ text: "", to_i: -1, msg_i: 0, voice: this.audioRoleKey.voice, prompt: this.audioRoleKey.prompt, msg_content: "好的，我来为您解答疑问。", all: true, toSpeechtest: 0, form: "getLocalData", })
      let localData = null
      // if(this.isLocalData){
      //   localData = await this.getLocalData(msgArr[0]); // 等待 Promise 完成
      //   if(!localData){
      //     this.$modal.msgWarning("本地数据获取失败");
      //   }
      // }
      this.chatUserCLickStream(msgArr[0], localData)
    },
    // 请求本地服务中的数据+整理最终请求数据
    async getLocalData(msgData) {
      try {
        const res = await getData(this.selVal.value, msgData.content); // 直接 await
        console.log("localLimit:", this.localLimit);
        let data = [];
        for (let i = 0; i < this.localLimit; i++) {
          data.push(res.documents[i]);
        }
        const localContent = `你是用于回答问题的助手。请使用以下检索到的上下文来回答问题。如果你不知道答案，就直接说你不知道，使用表情增强语气，请使用简体中文回答。问题： {${res.prompt}} 上下文：{${JSON.stringify(data)}} 回答:`;
        return { type: "human", name: "local", content: localContent };
      } catch (error) {
        console.error("获取数据失败:", error);
        return null;
      }
    },
    // stream:true 开始
    // 异步流式--提交
    async chatUserCLickStream(msgData, localData) {
      this.isThink = false // 调整传输数据状态
      let messages = [msgData]
      // if(localData){
      //   messages.push(localData)
      // }
      // addConfig(JSON.stringify(messages)).then(res => {
      //   console.log("存储json信息",res)
      // })
      // console.log("messages",messages)
      addConfig(messages).then(res => {
        console.log("提交json数据", res)
      })
      console.log(this.agent)
      console.log("messages", this.thread.thread_id, this.agent.assistant_id,
        { assistant_id: this.agent.assistant_id, input: { messages }, streamMode: ["updates", "messages"] })
      let a = langC.runsStream(
        this.thread.thread_id, this.agent.assistant_id,
        { assistant_id: this.agent.assistant_id, input: { messages }, streamMode: ["updates", "messages"] })

      for await (const chunk of a) {
        
        if (this.isContent && chunk.event == "messages/partial") {
          if (this.isEnd || this.isUserEnd) {
            return false
          }         
          this.getText(chunk.data[0].content)
          this.currentMessages[this.currentMessages.length - 1].generating = false
          if (chunk.data[0].response_metadata.finish_reason == "stop" || chunk.data[0].response_metadata.done_reason == "stop") {
            this.currentMessages[this.currentMessages.length - 1].finish = true
            this.isEnd = true // 重置状态
            this.isUserEnd = false
            this.isThink = false
            this.isContent = false
            console.error("重置状态")
          }

        } else if (chunk.event == "updates") {
          if (chunk.data.retrieve){
            this.isContent = true
            this.isThink = false
          }
          if (chunk.data.retrieve && chunk.data.retrieve.retrieved_docs.length) {
            const docs = chunk.data.retrieve.retrieved_docs
            for (let d = 0; d < docs.length; d++) {
              const str = docs[d].page_content
              if (str.indexOf('resStart:')>=0) {
                // const arrayStr = resDocs
                const arrayStr = str.split('resStart:')[1]; // 分割字符串，取数组部分
                // 2. 替换单引号为双引号（JSON 要求双引号）
                const jsonStr = arrayStr.split("'").join('"');
                // 3. 解析为 JSON 对象
                console.log("resDocs",jsonStr)
                this.currentMessages[this.currentMessages.length-1].resourceList = JSON.parse(jsonStr);
                break
              }
            }
          }
        } else if (chunk.event == "error") {
          this.isEnd = true // 重置状态
          this.isUserEnd = true
          this.isThink = false
          this.isContent = false
          this.$message.warning('网络异常，请重新提问！')
        }

        // console.log(chunk);
      }
    },
    // 异步提交后，处理返回数据
    async getText(content) {
      // 用户可能会中断问答
      if (this.isEnd || this.isUserEnd) {
        console.error("getText-回答中断[进程，用户]", this.isEnd, this.isUserEnd)
        return false
      }
      const msgI = this.currentMessages.length - 1 // 当前数据下标
      let msg = "关于[" + this.currentMessages[msgI - 1].content.replace(/\n/g, '') + "]的问题，您需要了解以下信息。\n " + content
      this.currentMessages[msgI].content = msg
      let msgArr = msg.split(this.toSpeechtest);

      // // 携带截取后的数据下标，保证异步写入顺序正常
      const key = this.toSpeechKey
      // console.log(msgI + "==msgI携带截取后的数据下标，保证异步写入顺序正常:", key + "<" + parseInt(msgArr.length - 1)
      // , msgArr.length, msgArr)
      if (key == 0 || key < parseInt(msgArr.length - 1)) {
        this.fetchToSpeech({ text: msgArr[key], to_i: key, msg_i: msgI, msg_content: "", all: false, voice: this.audioRoleKey.voice, prompt: this.audioRoleKey.prompt, form: "生成", })
        this.toSpeechKey++
      }
    },
    // stream:true 结束

    // 中断回答操作，重置数据
    cancelRequest() {
      this.isEnd = true;
      this.isUserEnd = true;
      // 重置语音状态
      this.audioStop(0)
      this.audioInit(0)
      // 消息列表
      this.currentMessages[this.currentMessages.length - 1].content = "您中断了回答，可以继续提问...."
      this.currentMessages[this.currentMessages.length - 1].generating = false
    },

    // 格式化返回内容
    upContent(text) {
      if (text) {
        return markdown.returnContent(text.replace('并且以中文回复，回复内容中不包含附件参考信息', ''))
      }
    },
    // 选择问答模块切换
    upSelVal(i) {
      this.selVal = this.selOp[i]
    },
    // 动态对话框
    scrollToBottom() {
      this.$nextTick(() => {
        const container = this.$refs.messageContainer
        container.scrollTop = container.scrollHeight
      })
    },
    // 启动重命名
    startRename(item, isRightSide = false) {
      this.renamingId = item.thread_id
      this.editText = item.metadata.ques
      this.originalText = item.metadata.ques

      this.$nextTick(() => {
        const refName = isRightSide ? 'renameInputRight' : 'renameInputLeft'
        if (this.$refs[refName] && this.$refs[refName][0]) {
          this.$refs[refName][0].focus()
        }
      })
    },

    // 提交重命名
    async submitRename(item) {
      if (this.renamingId && this.editText.trim() && this.editText !== this.originalText) {
        try {
          item.metadata.ques = this.editText
          await langC.threadUpdate(item.thread_id, { metadata: item.metadata })
          this.$message.success('重命名成功')
        } catch (error) {
          this.$message.error('重命名失败')
          item.metadata.ques = this.originalText
        }
      }
      this.cancelRename()
    },

    // 取消重命名
    cancelRename() {
      this.renamingId = null
      this.editText = ''
      this.originalText = ''
    },
    deleteHistory(thread_id) {
      this.$confirm('确定要删除该历史记录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        try {
          await langC.threadDelete(thread_id)
          this.$message.success('删除成功');
          this.initLang()
        } catch (error) {
          console.log(error)
        }
      })
    },
  }
}
</script>

<style scoped>
.main-container1 {
  display: flex;
  height: calc(100vh);
  padding: 55px;
  background: -webkit-linear-gradient(#C2DBFA, #DAE0FA, #F5F6FA);
  background: linear-gradient(#C2DBFA, #DAE0FA, #F5F6FA);
}

.main-card {
  box-sizing: border-box;
  width: 100%;
  background: #fff;
  border-radius: 15px;
  box-shadow: rgba(100, 100, 100, 0.1) 1px 2px 3px;
  position: relative;
}

.card-header {
  border-radius: 15px 15px 0 0;
  height: 100px;
  background: -webkit-linear-gradient(to right, #409eff, #B271B7, #409eff, #2E82FC, #1890FF, #409eff, #1890FF, #409eff);
  background: linear-gradient(to right, #409eff, #B271B7, #409eff, #2E82FC, #1890FF, #409eff, #1890FF, #409eff);
}

.icon-shouye {
  position: absolute;
  top: 25px;
  right: 25px;
  font-size: 25px;
  color: #fff;
  z-index: 5;
}

.header-img {
  height: 100%;
}

.header-img img {
  height: 120%;
}

.header-title {
  height: 100%;
  line-height: 80px;
  padding-left: 30px;
  color: #fff;
  font-size: 23px;
  letter-spacing: 8px;
}

.header-title>div {
  height: 100%;
  overflow: hidden;
}

.card-content {
  margin-top: -20px;
  background: #fff;
  height: calc(100% - 80px);
  display: flex;
  overflow: hidden;
  border-radius: 15px;

}

.card-content-left {
  width: calc(100% - 360px);
  position: relative;
  height: 100%;
  background: #F6F7FB;
}

.card-content-right {
  background: #fff;
  width: 360px;
  height: 100%;
  padding-left: 20px;
}

.right-title {
  position: relative;
  padding-left: 12px;
  line-height: 50px;
  font-size: 18px;
  color: rgba(0, 0, 0, 0.8);
}

.right-title::before {
  content: " ";
  position: absolute;
  top: 16px;
  left: 0;
  height: 18px;
  width: 3px;
  border-radius: 3px;
  background: #246BD4;
}

.right-content {
  padding-bottom: 40px;
}

.bianminBtn {
  width: 45%;
  background: #83D1F7;
  height: 45px;
  line-height: 45px;
  margin: 10px 5% 0 0;
}

.serveDiv {
  width: 45%;
  display: inline-block;
  box-sizing: border-box;
  line-height: 35px;
  margin: 10px 5% 0 0;
  text-align: center;
}

.rainbow-text {
  color: #000;
  /* 设置基础文字颜色 */
  background: -webkit-linear-gradient(to right, #1890FF, #181cff, #B271B7);
  background: linear-gradient(to right, #1890FF, #181cff, #B271B7);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.p-div {
  line-height: 35px;
  font-size: 15px;
  color: rgba(0, 0, 0, 0.8);
}

.history-List {
  line-height: 35px;
  font-size: 15px;
  color: rgba(0, 0, 0, 0.8);
  padding: 0px 30px;
  /* 右侧留出删除按钮空间 */
  display: flex;
  justify-content: space-between;
  position: relative;
}

.history-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  padding-right: 10px;
}

.history-List:hover {
  background: rgba(0, 0, 0, 0.08);
  /* 鼠标悬停背景色 */
}

.p-dian {
  position: relative;
  padding-left: 20px;
}

.p-dian::before {
  content: " ";
  position: absolute;
  top: 15px;
  left: 10px;
  height: 3px;
  width: 3px;
  border-radius: 3px;
  background: #246BD4;
}

.history-sidebar {
  width: 15%;
  background: #ffffff;
  box-shadow: 2px 0 6px rgba(0, 0, 0, 0.1);
  transition: width 0.3s ease;
  overflow: hidden;
}

.sidebar-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  border-bottom: 1px solid #ebeef5;
}

.history-list {
  height: calc(100vh - 75px);
  overflow-y: auto;
}

.history-item {
  padding: 12px;
  border-bottom: 1px solid #f0f2f5;
  cursor: pointer;
}

.history-item:hover {
  background: #f5f7fa;
}

.history-item.active {
  background: #e8f4ff;
}

.history-title {
  font-weight: 500;
  color: #303133;
  margin-bottom: 4px;
}

.history-preview {
  font-size: 14px;
  color: #909399;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.main-content {
  flex: 1;
  display: flex;
  height: 100%;
  flex-direction: column;
}

.message-container {
  flex: 1;
  overflow-y: auto;
  padding: 20px 0 190px;
}

.message-item {
  display: flex;
  margin-bottom: 20px;
}

.message-item.human {
  flex-direction: row-reverse;
}

.message-avatar {
  width: 40px;
  height: 40px;
  margin: 0 12px;
}

.message-avatar img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.message-bubble {
  max-width: 80%;
  overflow: hidden;
  padding: 10px 16px;
  border-radius: 20px;
  position: relative;
  box-shadow: rgba(100, 100, 100, 0.1) 2px 3px 5px;
}

.message-item.human .message-bubble {
  background: rgb(110, 177, 248);
  color: white;
  padding: 3px 16px 2px;
  margin-right: 10px;
}

.message-item.ai .message-bubble,
.message-item.system .message-bubble {
  background: white;
  border: 1px solid #ebeef5;
}

.message-text {
  line-height: 1.6;
}

.message-title {
  font-size: 1.5rem;
  font-weight: 700;
  font-family: 'Segoe UI', system-ui, sans-serif;
  text-align: center;
  position: relative;
  padding-bottom: 1rem;
  margin: 0.5rem 0;

  /* 渐变文字效果 */
  background: linear-gradient(45deg, #2c3e50, #3498db);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  text-fill-color: transparent;

  /* 文字阴影增强立体感 */
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);

  /* 底部装饰线 */
  &::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 80px;
    height: 3px;
    background: linear-gradient(90deg, transparent, #3498db, transparent);
  }

  /* 悬停动画效果 */
  transition: all 0.3s ease;

  &:hover {
    transform: translateY(-2px);
    text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.15);
  }
}

.message-content {
  display: flex;
  justify-content: center;
  div {
    display: flex;
    justify-content: center;
  }
}

.message-time {
  font-size: 12px;
  color: #909399;
  margin-top: 6px;
}

.input-div {
  background: #F6F7FB;
  padding: 15px 20%;
  box-shadow: 0 0 10px 5px #F6F7FB;
  position: absolute;
  bottom: 0;
  right: 0;
  left: 0;
}

.input-bg {
  background: #fff;
  border-radius: 20px;
  padding: 10px 15px;
  box-shadow: rgba(100, 100, 100, 0.1) 1px 2px 3px;
  position: relative;

}

.input-div {
  /* margin:0 150px 15px;
  display: flex; */
}

.input-area {
  position: relative;
  left: 25px;
  bottom: 5px;
  width: calc(100% - 110px);
}

.action-bar {
  margin-top: 12px;
  text-align: right;
}

pre {
  background: #f6f8fa;
  padding: 12px;
  border-radius: 4px;
  overflow-x: auto;
}

code {
  font-family: monospace;
}

/* 等待效果 */
/* 加载动画样式 */
.loading-spinner {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 30px;
}

.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid #f3f3f3;
  border-top: 2px solid #409eff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

/* 调整消息气泡样式 */
.message-bubble.generating {
  min-height: 80px;
  padding: 20px;
}


.spinner {
  width: 28px;
  /* 调整大小 */
  height: 28px;
  border-width: 4px;
  /* 调整边框粗细 */
  border-top-color: #67C23A;
  /* 修改颜色 */
  animation-duration: 0.6s;
  /* 调整旋转速度 */
}


/* 样式优化 */
.leftDiv {
  width: 150px;
  margin: 3px 15px 0 0;
}

.iconBtn {
  color: #727272;
}

.message-text .think {
  background: #f5f5f5;
  color: #727272;
  font-size: 14px;
  padding: 20px;
  border-radius: 10px;
}

.stAudio {
  position: fixed;
  top: -20%;
  right: -300px;
  opacity: 0;
  z-index: -5;
}

.auto-audio-role img {
  width: 50px;
  display: block;
  margin-bottom: 5px;
}

.auto-audio-role button {
  padding: 0 0 5px;
  overflow: hidden;
}


.auto-audio-role.rensheng button {
  width: 46%;
  margin: 2%;
}

.auto-audio-role.rensheng button img {
  width: 100%;
}






/* 右侧消失，左侧全屏 */
@media(max-width:1400px) {
  .card-content-right {
    display: none;
  }

  .card-content-left {
    width: 100%;
  }
}

@media (max-width: 768px) {
  .message-title {
    font-size: 2rem;
    margin: 0.2rem 0;
  }
}


@media(min-width:993px) {
  .input-bg {
    padding: 25px 15px;
  }
}

@media(max-width:992px) {

  /* 政府页面效果去除 */
  .card-header {
    display: none;
  }

  .card-content {
    margin-top: 0;
  }

  .main-container1 {
    padding: 0;
  }

  .app-main,
  .card-content {
    height: 100vh !important;
  }

  .card-content {
    border-radius: 0;
  }


  /* .input-div{margin:0;} */
  .message-container {
    padding: 50px 0 150px;
  }

  /* .Btn{height:30px;width:30px;display: block;} */
  .input-div {
    padding: 10px 15px 5px
  }

  /* 对话框 */
  .message-item {
    margin-bottom: 5px;
  }

  .message-bubble {
    max-width: calc(100vw - 45px);
  }

  .message-item.human .message-bubble {
    margin: 5px 5px 5px 0;
  }

  .message-item.system .message-bubble,
  .message-item.ai .message-bubble {
    margin: 5px 0 5px 5px;
  }

  .message-avatar {
    width: 30px;
    height: 30px;
    margin: 0 0 0 5px;
  }

  .icon-shouye {
    top: 15px;
    right: 10px;
    color: rgba(0, 0, 0, 0.7);
  }
}
</style>